# -*- python -*-

# This is a sample buildmaster config file. It must be installed as
# 'master.cfg' in your buildmaster's base directory (although the filename
# can be changed with the --basedir option to 'mktap buildbot master').

# It has one job: define a dictionary named BuildmasterConfig. This
# dictionary has a variety of keys to control different aspects of the
# buildmaster. They are documented in docs/config.xhtml .


# This file was copied and modified from buildbot/master/master.cfg.


import os
from buildbot.scheduler import Scheduler
from buildbot.process.step import ShellCommand
from buildbot.process import step, factory
from buildbot.status import html
from twisted.python import log
import Feeder
s = factory.s

# This is the dictionary that the buildmaster pays attention to. We also use
# a shorter alias to save typing.
c = BuildmasterConfig = {}

# the 'bots' list defines the set of allowable buildslaves. Each element is a
# tuple of bot-name and bot-password. These correspond to values given to the
# buildslave's mktap invocation.
from secrets import bot_name_passwd
c['bots'] = [
             bot_name_passwd("gheorghiu-x86"),	
             bot_name_passwd("gheorghiu-2-x86"),	
             bot_name_passwd("sanghyeon-x86"),
             bot_name_passwd("bear-x86"),
             bot_name_passwd("osaf-x86"),
             bot_name_passwd("manuzhai-x86"),
             bot_name_passwd("murphy-amd64"),
             bot_name_passwd("dasilva-x86"),
             bot_name_passwd("flanagan-sparc"),
             bot_name_passwd("montanaro-g5"),
             bot_name_passwd("mcneil-x86"),
#             bot_name_passwd("ward-x86"),
#             bot_name_passwd("egenix-x86"),
            ]

# the 'sources' list tells the buildmaster how it should find out about
# source code changes. Any class which implements IChangeSource can be added
# to this list: there are several in buildbot/changes/*.py to choose from.

c['sources'] = []

from buildbot.changes.pb import PBChangeSource
c['sources'].append(PBChangeSource(prefix='python'))

# the 'builders' list defines the Builders. Each one is configured with a
# dictionary, using the following keys:
#  name (required): the name used to describe this builder
#  slavename (required): which slave to use, must appear in c['bots']
#  builddir (required): which subdirectory to run the builder in
#  factory (required): a BuildFactory to define how the build is run
#  periodicBuildTime (optional): if set, force a build every N seconds

# We use one scheduler per branch (as the AnyBranchScheduler is useless)
# and one builder per slave and branch

class Clean(step.ShellCommand):
    name = "clean"
    warnOnFailure = 1
    description = ["cleaning"]
    descriptionDone = ["clean"]
    command = ["make", "distclean"]

class Kill(step.ShellCommand):
    name = "kill.python"
    warnOnFailure = 1
    description = ["killing python"]
    descriptionDone = ["kill.python"]
    command = ["make", "-C", "Tools/buildbot"]

class MakeInstall(ShellCommand):
    haltOnFailure = 1
    name = "make install"
    description = ["running %s" % name]
    descriptionDone = [name]


# Directory where the 3rd party software lives that we will test.
_TOOL_DIR = "../../python-tool/"

# Where we will install Python (must be absolute for configure).
_INSTALL_DIR = '/tmp/python-buildbot/local'

class TestMailNotificationBuild(factory.GNUAutoconf):
    def __init__(self, source, projects):
        for project_name in projects:
            step_name = project_name + " tests"
            if project_name == 'prepare':
                step_name = 'pre-requisites installation'
            cmd = ["%s/bin/python" % _INSTALL_DIR, "%s/run_tests.py" % _TOOL_DIR, project_name]
            class ToolTest(ShellCommand):
                flunkOnFailure = 1
                name = project_name
                description = ["running %s" % step_name]
                descriptionDone = [step_name]
            self.steps.append(s(ToolTest, command=cmd, timeout=60*60))

class UnixPybotBuild(factory.GNUAutoconf):
    def __init__(self, source, projects):
        #cfg_flags = ["--with-pydebug", "--prefix=%s" % _INSTALL_DIR]
        cfg_flags = ["--prefix=%s" % _INSTALL_DIR]
        factory.GNUAutoconf.__init__(self, source,
                                     configureFlags=cfg_flags,
                                     test=None)
        cmd = ["make", "test"]
        self.steps.append(s(step.Test, command=cmd, timeout=15*60, haltOnFailure=True))

        cmd = ["make", "install"]
        self.steps.append(s(MakeInstall, command=cmd, timeout=5*60))

        # XXX(nnorwitz): this doesn't work on a Mac where it's python.exe
        for project_name in projects:
            step_name = project_name + " tests"
            if project_name == 'prepare':
                step_name = 'pre-requisites installation'
            cmd = ["%s/bin/python" % _INSTALL_DIR, "%s/run_tests.py" % _TOOL_DIR, project_name]
            class ToolTest(ShellCommand):
                flunkOnFailure = 1
                name = project_name
                description = ["running %s" % step_name]
                descriptionDone = [step_name]
            self.steps.append(s(ToolTest, command=cmd, timeout=60*60))

        self.steps.append(s(Clean))

class CygwinBuild(UnixPybotBuild):
    def __init__(self, source):
        UnixPybotBuild.__init__(self, source)
        self.steps.insert(1, s(Kill))
        # XXX(nnorwitz): test_bsddb3 crashes the interpreter on cygwin.
        # Ignore test_bsddb3 to see if we can get some real results.
        test_step = self.steps[-4][1]
        test_step['env'] = {'EXTRATESTOPTS': '-uall -rw -x test_bsddb3 test_logging'}

class WindowsPybotBuild(factory.BuildFactory):
    def __init__(self, source, projects):
        bb_script_dir = 'Tools\\buildbot\\'
        external_tool_dir = _TOOL_DIR.replace('/', '\\')
        factory.BuildFactory.__init__(self, [source])
        self.steps.append(s(step.Compile, command=[bb_script_dir + 'build.bat']))
        self.steps.append(s(step.Test, command=[bb_script_dir + 'test.bat']))
        for project in projects:
            project_name, release = project.split()
            step_name = project + " tests"
            if project_name == 'prepare':
                step_name = 'pre-requisites installation'
            cmd = [external_tool_dir + '%s.bat %s' % (project_name, release)]
            class ToolTest(ShellCommand):
                flunkOnFailure = 1
                name = project
                description = ["running %s" % step_name]
                descriptionDone = [step_name]
            self.steps.append(s(ToolTest, command=cmd, timeout=2*60*60))
        self.steps.append(s(Clean, command=[bb_script_dir + 'clean.bat']))

baseURL = "http://svn.python.org/projects/python/"
branches = [("trunk", "trunk"), 
            ("branches/release25-maint", "2.5"),
           ]

EMAIL = 1
NOEMAIL = 0
builders = [
            ("x86 Red Hat 9", "gheorghiu-x86", UnixPybotBuild, ["Twisted.zope.stable", "Twisted.zope.dev", "bzr.dev"], EMAIL),
            ("x86 Debian Unstable", "sanghyeon-x86", UnixPybotBuild, ["docutils", "roundup", "Kid"], EMAIL),
            ("x86 Ubuntu Edgy", "bear-x86", UnixPybotBuild, ["parsedatetime"], EMAIL),
            ("x86 OSX", "osaf-x86", UnixPybotBuild, ["vobject", "zanshin"], EMAIL),
            ("x86 Gentoo", "manuzhai-x86", UnixPybotBuild, ["prepare", "pysqlite", "Genshi", "Trac", "feedvalidator"], EMAIL),
            ("amd64 Ubuntu Gutsy", "murphy-amd64", UnixPybotBuild, ["MySQLdb", "CherryPy", "Storm"], EMAIL),
            ("x86 Windows 2003", "dasilva-x86", WindowsPybotBuild, ["setuptools dev", "setuptools stable", "lxml stable", "lxml dev", "bzr stable", "bzr dev"], EMAIL),
            ("x86 Ubuntu Breezy", "gheorghiu-2-x86", UnixPybotBuild, ["nose", "py.lib", "Cheesecake", "twill"], EMAIL),
#            ("x86 Ubuntu Breezy", "gheorghiu-2-x86", UnixPybotBuild, ["nose", "py.lib", "Testoob", "Cheesecake", "twill"], EMAIL),
#            ("x86 Ubuntu Breezy", "gheorghiu-2-x86", TestMailNotificationBuild, ["Cheesecake"], EMAIL),
            ("sparc Solaris 10", "flanagan-sparc", UnixPybotBuild, ["Django"], EMAIL),
            ("x86 Fedora Core 6", "mcneil-x86", UnixPybotBuild, ["nose"], EMAIL),
            ("g5 OSX", "montanaro-g5", UnixPybotBuild, ["SQLAlchemy"], EMAIL),
#            ("x86 Debian", "ward-x86", UnixPybotBuild, ["Optik"], NOEMAIL),
#            ("x86 Suse 10.1", "egenix-x86", UnixPybotBuild, ["egenix"], NOEMAIL),
           ]

c['builders'] = []
c['schedulers'] = []

# Prevent multiple branches from building simultaneously.
from buildbot import locks
cpulock = locks.SlaveLock("cpu")

def is_important_file(filename):
    # Filename appears to be trunk/XXX or 2.4/XXX
    # Ignore changes from these directories, buildbot doesn't handle them.
    for prefix in ('/Misc/', '/Doc/', '/Demo/'):
        if filename.find(prefix) >= 0:
            return False
    return True

def is_important_change(change):
    # If any file is important, the change is important.
    for filename in change.files:
        if is_important_file(filename):
            return True
    return False

for branch, branchname in branches:
    buildernames = []
    for name, slave, buildfactory, projects, wants_email in builders:
        buildername = name + " " + branchname
        source = s(step.SVN, baseURL=baseURL, defaultBranch=branch,
            mode="update")
        f = buildfactory(source, projects)
        buildernames.append(buildername)
        c['builders'].append({
                'name' : buildername,
                'slavename' : slave,
                'builddir' : '%s.%s' % (branchname, slave),
                'factory' : f,
                'category' : branchname,
                'locks' : [cpulock]
                })
    c['schedulers'].append(Scheduler(name=branchname, 
                                 branch=branch,
                                 treeStableTimer=5*60, # seconds
                                 builderNames=buildernames,
                                 fileIsImportant=is_important_change))

# 'slavePortnum' defines the TCP port to listen on. This must match the value
# configured into the buildslaves (with their --master option)

c['slavePortnum'] = 9070

# 'status' is a list of Status Targets. The results of each build will be
# pushed to these targets. buildbot/status/*.py has a variety to choose from,
# including web pages, email senders, and IRC bots.

buildbot_css = "/data/community/buildbot/python.css"

c['status'] = status = []
allowForce = True
portBase = 9060
for i, categories in enumerate([None, 'trunk', '2.5']):
    http_port = 'tcp:%d:interface=127.0.0.1' % (portBase + i)
    kwds = {}
    if categories:
        kwds['categories'] = categories
#    status.append(html.Waterfall(http_port=http_port, allowForce=allowForce,
#                                 css=buildbot_css, **kwds))
    status.append(Feeder.WaterfallWithFeeds(http_port=http_port, 
    				allowForce=allowForce,
				css=buildbot_css, **kwds))

from mymail import MailNotifier
from secrets import get_email_list
for name, slave, buildfactory, projects, wants_email in builders:
	if not wants_email:
		continue
	email_list = get_email_list(slave)
	if not email_list:
		continue
	log.msg("sending mail to %s" % " ".join(email_list))
	for branch, branchname in branches:
	    buildername = name + " " + branchname
	    c['status'].append(MailNotifier(
				fromaddr="nobody@python.org",
				builders=[buildername],
				extraRecipients=email_list,
				sendToInterestedUsers=False,
#                                mode="problem",
                                mode="failing",
                                relayhost="mail.python.org"
                                ))
# Enable IRC notifications
from buildbot.status import words
status.append(words.IRC(host="irc.freenode.net:6667", nick="pybot-bb",
                         channels=["#pybots"], allowForce=False))


# Enable statusgui notifications
import buildbot.status.client
pbl = buildbot.status.client.PBListener(port=9071)
status.append(pbl)

# if you set 'debugPassword', then you can connect to the buildmaster with
# the diagnostic tool in contrib/debugclient.py . From this tool, you can
# manually force builds and inject changes, which may be useful for testing
# your buildmaster without actually commiting changes to your repository (or
# before you have a functioning 'sources' set up). The debug tool uses the
# same port number as the slaves do: 'slavePortnum'.

c['debugPassword'] = "tivertio"

# if you set 'manhole', you can telnet into the buildmaster and get an
# interactive python shell, which may be useful for debugging buildbot
# internals. It is probably only useful for buildbot developers.
# from buildbot.master import Manhole
# c['manhole'] = Manhole(9999, "admin", "oneddens")

# the 'projectName' string will be used to describe the project that this
# buildbot is working on. For example, it is used as the title of the
# waterfall HTML page. The 'projectURL' string will be used to provide a link
# from buildbot HTML pages to your project's home page.

c['projectName'] = "Pybots"
c['projectURL'] = "http://www.pybots.org/"

# the 'buildbotURL' string should point to the location where the buildbot's
# internal web server (usually the html.Waterfall page) is visible. This
# typically uses the port number set in the Waterfall 'status' entry, but
# with an externally-visible host name which the buildbot cannot figure out
# without some help.

c['buildbotURL'] = "http://www.python.org/dev/buildbot/community/all/"
